import { Injectable, HttpService, HttpException, HttpStatus } from '@nestjs/common'
import { createHash } from 'crypto' // 依赖nodejs版本 getHashes-查看支持的算法
import { writeFileText, readFileText } from '../common'
import config from '../config'
import constants from '../constants'
import { AccessTokenData, SendTemplateDto } from './dto'
import { resolve } from 'path'

@Injectable()
export class WechatService {
  private accessTokenData: AccessTokenData
  constructor(private readonly httpService: HttpService) {
    this.accessTokenData = {
      accessToken: '',
      expiresIn: 0,
    }
  }
  checkSignature(timestamp: string, nonce: string, signature: string, echostr: string): string {
    // 1）将token、timestamp、nonce三个参数进行字典序排序
    // 2）将三个参数字符串拼接成一个字符串进行sha1加密
    // 3）开发者获得加密后的字符串可与signature对比，标识该请求来源于微信
    const arr = [config.wechat.gzh.token, timestamp, nonce]
    const paramArr = arr.sort()
    const hash = createHash('sha1')
    hash.update(paramArr.join(''))
    const hex = hash.digest('hex')
    if (hex === signature) {
      return echostr
    } else {
      return 'fail'
    }
  }
  async getAccessToken() {
    const params = {
      grant_type: 'client_credential',
      appid: config.wechat.gzh.appid,
      secret: config.wechat.gzh.appsecret,
    }
    let { accessToken, expiresIn } = this.accessTokenData
    // TODO: 生产放内存, 其他环境写文件
    const env = process.env.NODE_ENV
    if (env !== 'prod') {
      const resStr = await readFileText(resolve(__dirname, './', 'accessToken.txt'))
      const resAccessToken = JSON.parse(resStr)
      accessToken = resAccessToken.accessToken || ''
      expiresIn = resAccessToken.expiresIn || 0
    }
    // 写入数据fun
    const setAccessToken = async (env, target, data) => {
      // 到期时间/秒 提前5分钟获取新accessToken
      const expires_s = data.expires_in - 300
      // 到期时间/ms
      const expiresIn = new Date().valueOf() + expires_s * 1000
      if (env === 'prod') {
        target.accessToken = data.access_token
        target.expiresIn = expiresIn
      } else {
        const save = {
          accessToken: data.access_token,
          expiresIn: expiresIn,
        }
        await writeFileText(resolve(__dirname, './', 'accessToken.txt'), JSON.stringify(save))
      }
      return { accessToken: data.access_token, expiresIn }
    }
    // 过期检查fun
    const isExpires = () => {
      if (!expiresIn) return true
      return new Date().valueOf() > expiresIn
    }
    // 过期检查
    if (!isExpires()) return { accessToken, expiresIn }
    // 刷新accessToken
    console.log(`token expires`)
    const { data } = await this.httpService
      .get(constants.API.WX_API.GET_ACCESS_TOKEN, {
        params,
      })
      .toPromise()
    if (data.access_token && data.expires_in) {
      return await setAccessToken(env, this.accessTokenData, data)
    } else {
      const errStr = `server: getAccessToken errmsg: ${data.errmsg}`
      throw new HttpException(errStr, HttpStatus.SERVICE_UNAVAILABLE)
    }
  }
  async getServersIP(accessToken) {
    const params = {
      access_token: accessToken,
    }
    const { data } = await this.httpService
      .get(constants.API.WX_API.GET_SERVERS_IP, {
        params,
      })
      .toPromise()
    return data
  }
  async sendTemplate({
    accessToken,
    touser, // openid
    template_id,
    productType,
    name,
    number,
    expDate,
    remark,
  }: SendTemplateDto) {
    const body = {
      touser: touser,
      template_id: template_id,
      url: 'http://www.cctv.com',
      topcolor: '#FF0000',
      data: {
        productType: {
          value: productType,
          color: '#173177',
        },
        name: {
          value: name,
          color: '#173177',
        },
        number: {
          value: number,
          color: '#173177',
        },
        expDate: {
          value: expDate,
          color: '#173177',
        },
        remark: {
          value: remark,
          color: '#173177',
        },
      },
    }
    const req = await this.httpService
      .post(`${constants.API.WX_API.SEND_TEMPLATE}?access_token=${accessToken}`, body)
      .toPromise()
    return req.data
  }
  async getTicket(accessToken, url) {
    const { data } = await this.httpService
      .get(`${constants.API.WX_API.GET_TICKET}?access_token=${accessToken}&type=jsapi`)
      .toPromise()
    if (data.ticket) {
      const e = {
        noncestr: 'Wm3WZYTPz0wzccnW',
        signature: '',
        timestamp: Date.now(),
        url,
      }
      const arr = [
        'jsapi_ticket=' + data.ticket,
        'noncestr=' + e.noncestr,
        'timestamp=' + e.timestamp,
        'url=' + e.url,
      ]
      const str1 = arr.join('&')
      const hash = createHash('sha1')
      hash.update(str1)
      const hex = hash.digest('hex')
      e.signature = hex
      return e
    } else {
      return {}
    }
  }
}
